// @Name    : cai_base_http_request
// @author  : simon
// @date    : 2023/10/11 13:56
// @version : 1.0
// @desc :单例模式管理，复用Dio实例避免重复创建。增强错误处理：统一异常处理流程；
// 支持网络状态监测；支持请求取消组合管理；
// 1.避免全局单例模式
// 问题：使用静态manager单例会导致全局状态共享，多个请求间可能互相干扰（如cancelToken冲突）。
// 优化：改用实例方法，允许创建多个实例或通过依赖注入管理，提升灵活性和可测试性。
// 2.取消请求的Token管理
// 问题：全局cancelToken可能导致多个请求共用一个Token，取消时会误伤其他请求。
// 优化：每个请求独立生成CancelToken，并在请求结束后释放，避免交叉影响。
// 3.将 Toast 提示直接耦合在网络库中是不合适的。
// 1）违反单一职责原则
// 你的网络库 (CaiHttpRequest) 现在承担了两个职责：
// 网络通信：处理 HTTP 请求和响应
// UI 展示：通过 Toast 显示消息
// 2）紧耦合
// 对 UI 的依赖：网络层现在直接依赖 CAIToast 这个具体的 UI 组件
// 解决方案：1）使用回调；简单易实现，改动小	仍然有一定耦合	小型项目，快速开发
// 2）事件机制；完全解耦，高度灵活	需要管理订阅	中型项目，需要高度解耦
// 3）依赖注入；最优雅，易于测试	需要更多代码	大型项目，需要测试和灵活替换

import 'dart:io';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flutter/foundation.dart';
import 'cai_network_notification_center.dart';
import 'cai_error_handlers.dart';
import 'cai_network_monitor.dart';
import 'cai_request_config.dart';
import 'cai_request_cancel_group.dart';
import 'cai_base_http_port_configure.dart';
import 'cai_base_http_user_configure.dart';
import 'cai_base_http_proxy_config.dart';
export 'cai_base_http_proxy_config.dart';
export 'cai_base_http_port_configure.dart';
export 'cai_base_http_user_configure.dart';
export 'cai_http_request_extension.dart';
export 'cai_request_cancel_group.dart';
export 'package:dio/dio.dart';
export 'cai_network_notification_center.dart';

// 常量配置类
// 使用静态常量减少对象创建
class CaiHttpConfig {
  static const connectTimeout = Duration(seconds: 10);
  static const receiveTimeout = Duration(seconds: 30);
  static const sendTimeout = Duration(seconds: 20);
  static const successKey = 'success';
  static const dataKey = 'data';
  static const messageKey = 'msg';
  static const codeKey = 'code';
  static const tokenErrorCodes = {'TOKEN_EXIST', 'TOKEN_EMPTY'};
  static const String tokenErrorNotification = 'tokenErrorNotification';
}

class CaiHttpRequest {
  static final CaiHttpRequest shared = CaiHttpRequest._internal();
  final Dio _dio = Dio();
  String? _baseUrl;
  bool _isInitialized = false;

  CaiHttpRequest._internal();

  set baseUrl(String? url) => _baseUrl = url;

  String get baseUrl => _baseUrl ?? '';

  // 在类顶部添加配置项，请求数据之前是否先进行网络检查。
  bool enableNetworkCheck = true;

  // 添加错误处理回调
  static Function(String message)? onError;

  //复用Dio实例避免重复创建
  //优化拦截器配置
  Future<void> initialize() async {
    try {
      if (_isInitialized) return;
      await _configureDio();
      _setupInterceptors();
      _isInitialized = true;
    } catch (e) {
      rethrow;
    }
  }

  // 添加网络检查配置方法
  void configNetworkCheck(bool enable) {
    enableNetworkCheck = enable;
  }

  Future<void> changeConfig() async {
    await _configureDio();
  }

  //预配置网络适配器
  Future<void> _configureDio() async {
    try {
      final baseUrl = await _buildRequestConfig();

      _dio.options = BaseOptions(
        baseUrl: baseUrl,
        //请求服务器时间超时
        connectTimeout: CaiHttpConfig.connectTimeout,
        //接收数据时间，应该设置比服务端的长，不然总是报receiveTimeout。默认不限制`null` or
        // `Duration.zero` means no timeout limit.
        receiveTimeout: CaiHttpConfig.receiveTimeout,
        sendTimeout: CaiHttpConfig.sendTimeout,
        headers: await _buildHeaders(),
      );
      //默认_dio.httpClientAdapter = HttpClientAdapter();
      if (await _shouldEnableProxy()) {
        _dio.httpClientAdapter = await _createProxyAdapter();
      }
    } catch (e) {
      if (kDebugMode) {
        print(e);
      }
      rethrow;
    }
  }

  Future<String> _buildRequestConfig() async {
    final port = await CaiBaseHttpPortConfigure.getCompanyPortNum();
    return CaiBaseHttpPortConfigure.hasPortInUrl(baseUrl)
        ? baseUrl
        : '$baseUrl:${port ?? ''}';
  }

  Future<Map<String, dynamic>> _buildHeaders() async {
    String token = await CaiBaseHttpUserConfigure.getToken() ?? '';
    return {
      'RUK_TOKEN': token,
    };
  }

  //是否启用代理
  Future<bool> _shouldEnableProxy() async {
    return await CaiBaseHttpProxyConfig.getProxySwitch();
  }

  Future<IOHttpClientAdapter> _createProxyAdapter() async {
    String? proxyIp = await CaiBaseHttpProxyConfig.getProxyIp();
    String? proxyPort = await CaiBaseHttpProxyConfig.getProxyPort();
    return IOHttpClientAdapter(
      createHttpClient: () {
        final client =
            HttpClient(context: SecurityContext(withTrustedRoots: false));
        client.findProxy = (uri) => 'PROXY ${proxyIp ?? ''}:${proxyPort ?? ''}';
        client.badCertificateCallback = _allowSelfSignedCert;
        return client;
      },
    );
  }

  bool _allowSelfSignedCert(X509Certificate cert, String host, int port) =>
      true;

  void _setupInterceptors() {
    _dio.interceptors.add(LogInterceptor(
      request: kDebugMode,
      requestBody: true,
      requestHeader: kDebugMode,
      responseBody: kDebugMode,
      error: kDebugMode,
    ));
  }

  // 注意：传list格式的字典，必须设置范性Map，不然默认会以dynamic传值报错。
  // 例如：List<Map> list = [
  //       {'taskId': model.taskId, 'address': model.deliveryAddress}
  //     ];
  static Future<dynamic> postRequest<T>(String path,
      {Object? data,
      CancelToken? cancelToken,
      CaiRequestCancelGroup? cancelGroup,
      ProgressCallback? onSendProgress,
      bool? showToast = true}) async {
    return await shared.post(path,
        data: data,
        cancelToken: cancelToken,
        cancelGroup: cancelGroup,
        onSendProgress: onSendProgress,
        showErrorToast: showToast);
  }

  static Future<dynamic> getRequest<T>(String path,
      {Map<String, dynamic>? queryParameters,
      CancelToken? cancelToken,
      CaiRequestCancelGroup? cancelGroup,
      ProgressCallback? onSendProgress,
      bool? showToast = true}) async {
    return await shared.get(path,
        queryParameters: queryParameters,
        cancelToken: cancelToken,
        cancelGroup: cancelGroup,
        onSendProgress: onSendProgress,
        showErrorToast: showToast);
  }

  Future<dynamic> post<T>(String path,
      {Object? data,
      CancelToken? cancelToken,
      CaiRequestCancelGroup? cancelGroup,
      ProgressCallback? onSendProgress,
      bool? showErrorToast}) async {
    return request('POST', path,
        data: data,
        cancelToken: cancelToken,
        cancelGroup: cancelGroup,
        onSendProgress: onSendProgress,
        showErrorToast: showErrorToast);
  }

  Future<dynamic> get<T>(String path,
      {Map<String, dynamic>? queryParameters,
      CancelToken? cancelToken,
      CaiRequestCancelGroup? cancelGroup,
      ProgressCallback? onSendProgress,
      bool? showErrorToast}) async {
    return request('GET', path,
        queryParameters: queryParameters,
        cancelToken: cancelToken,
        cancelGroup: cancelGroup,
        onSendProgress: onSendProgress,
        showErrorToast: showErrorToast);
  }

  Future<dynamic> request<T>(String method, String path,
      {Object? data,
      Map<String, dynamic>? queryParameters,
      CancelToken? cancelToken,
      CaiRequestCancelGroup? cancelGroup, // 新增组合参数
      ProgressCallback? onSendProgress,
      bool? showErrorToast}) async {
    final effectiveToken = cancelGroup?.createToken() ?? cancelToken;
    try {
      if (enableNetworkCheck) {
        final hasNetwork = await CaiNetworkMonitor.isNetworkAvailable();
        if (!hasNetwork) {
          throw DioException(
            requestOptions: RequestOptions(path: path),
            message: '网络连接不可用，请检查网络设置',
            type: DioExceptionType.connectionError,
            error: 'NETWORK_UNAVAILABLE',
          );
        }
      }
      Response<T> response = await _dio.request(path,
          data: data,
          queryParameters: queryParameters,
          options: Options(method: method),
          cancelToken: effectiveToken,
          onReceiveProgress: onSendProgress);
      return _handleSuccessResponse(response);
    } on DioException catch (e) {
      throw _handleDioError(e, showErrorToast);
    } catch (e) {
      throw _handleGenericError(e, showErrorToast);
    }
  }

  // 新增批量请求方法
  Future<List<Response>> requestAll<T>(
    List<CaiRequestConfig> configs, {
    CaiRequestCancelGroup? cancelGroup,
  }) async {
    final group = cancelGroup ?? CaiRequestCancelGroup();
    final futures = configs
        .map((config) => request<T>(
              config.method,
              config.path,
              data: config.data,
              queryParameters: config.queryParameters,
              cancelGroup: group,
            ))
        .toList();

    try {
      return await Future.wait(futures as Iterable<Future<Response>>);
    } catch (e) {
      group.cancel('BatchError');
      rethrow;
    }
  }

  //处理返回数据
  dynamic _handleSuccessResponse<T>(Response<T> response) {
    final data = response.data;
    if (data is Map && data[CaiHttpConfig.successKey] == true) {
      return data[CaiHttpConfig.dataKey];
    }
    //业务失败
    throw _createBusinessException(response);
  }

  DioException _createBusinessException(Response response) {
    final data = response.data as Map;
    final code = data[CaiHttpConfig.codeKey];
    final message = data[CaiHttpConfig.messageKey];

    if (CaiHttpConfig.tokenErrorCodes.contains(code)) {
      CaiNetworkNotificationCenter.defaultCenter.postNotification(
        const CaiNetworkNotification(name: CaiHttpConfig.tokenErrorNotification),
      );
    }
    return DioException(
      requestOptions: response.requestOptions,
      response: response,
      message: message,
      error: code,
      type: DioExceptionType.unknown,
    );
  }

  DioException _handleDioError(DioException e, bool? showErrorToast) {
    final handler = ErrorHandlerFactory.getHandler(e);
    DioException exception = handler.handle(e);
    if (exception.type == DioExceptionType.cancel) {
      if (kDebugMode) {
        print('用户主动取消请求');
      }
    } else {
      if (showErrorToast != null && showErrorToast == true) {
        // 替换为回调
        onError?.call(exception.message ?? '未知错误');
      }
    }
    return exception;
  }

  //自定义Format异常，转为Dio异常
  DioException _handleGenericError(dynamic e, bool? showToast) {
    DioException exception;
    if (e is FormatException) {
      exception = FormatErrorHandler().handle(e);
      if (kDebugMode) {
        print('【e】: $e');
      }
    } else {
      exception = DioException(
        requestOptions: RequestOptions(path: ''),
        error: e,
        message: 'Unexpected error occurred',
      );
    }
    if (showToast != null && showToast == true) {
      // 替换为回调
      onError?.call(exception.message ?? '未知错误');
    }
    return exception;
  }

  // 统一文件上传方法:
  // path:上传服务器路径；
  // file:如果是filePath，则MultipartFile.fromFile生成MultipartFile；
  //如果是List<int> bytes，则MultipartFile.fromBytes(bytes, filename: 'xxx.jpg');
  static Future<dynamic> uploadFile<T>(
      {required String path,
      required dynamic file,
      CancelToken? cancelToken,
      ProgressCallback? onSendProgress,
      bool? showErrorToast}) async {
    MultipartFile multipartFile;
    if (file is String) {
      // multipartFile = await MultipartFile.fromFile(file);
      multipartFile = await MultipartFile.fromFile(file, filename: 'xxx.jpg');
    } else if (file is List<int>) {
      // multipartFile = MultipartFile.fromBytes(file);
      multipartFile = MultipartFile.fromBytes(file, filename: 'xxx.jpg');
    } else {
      throw ArgumentError('Unsupported file type');
    }
    FormData formData = FormData.fromMap({'file': multipartFile});
    return shared.post(
      path,
      data: formData,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      showErrorToast: showErrorToast,
    );
  }
}

/*
// 错误回调
// 在使用处配置回调
void main() {
  CaiHttpRequest.onError = (message) {
    CAIToast.showToast(message);
  };
}
 */